#include "CloudDataPubSubTypes.h"
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/topic/TypeSupport.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/DataReaderListener.hpp>
#include <fastdds/dds/subscriber/qos/DataReaderQos.hpp>
#include <fastdds/dds/subscriber/SampleInfo.hpp>
#include "util/file.h"

using namespace eprosima::fastdds::dds;
using namespace std;
using namespace base::utils;

class CloudDataSubscriber
{
private:
    DomainParticipant *participant_;

    Subscriber *subscriber_;

    Topic *topic_;

    DataReader *reader_;

    TypeSupport type_;

    class SubListener: public DataReaderListener
    {
    public:
        SubListener()
            : samples_(0)
        {}

        ~SubListener() override
        {}

        void on_subscription_matched(DataReader *reader,
                                     const SubscriptionMatchedStatus &info) override
        {
            if (info.current_count_change == 1)
            {
                std::cout << "Subscriber matched." << std::endl;
            }
            else if (info.current_count_change == -1)
            {
                std::cout << "Subscriber unmatched." << std::endl;
            }
            else
            {
                std::cout << info.current_count_change
                          << " is not a valid value for SubscriptionMatchedStatus current count change"
                          << std::endl;
            }
        }

        void on_data_available(DataReader *reader) override
        {
            SampleInfo info;
            if (reader->take_next_sample(&cloud_data_, &info) == ReturnCode_t::RETCODE_OK)
            {
                if (info.valid_data)
                {
                    samples_++;
                    cout << "height: " << cloud_data_.height() << " width: " << cloud_data_.width() << ", data size: " << cloud_data_.data().size()
                         << endl;
                }
            }
            // std::this_thread::sleep_for(std::chrono::milliseconds(10000));
        }

        CloudData cloud_data_;

        std::atomic_int samples_;

    } listener_;

public:
    CloudDataSubscriber()
        : participant_(nullptr), subscriber_(nullptr), topic_(nullptr), reader_(nullptr), type_(new CloudDataPubSubType())
    {}

    virtual ~CloudDataSubscriber()
    {
        if (reader_ != nullptr)
        {
            subscriber_->delete_datareader(reader_);
        }
        if (topic_ != nullptr)
        {
            participant_->delete_topic(topic_);
        }
        if (subscriber_ != nullptr)
        {
            participant_->delete_subscriber(subscriber_);
        }
        DomainParticipantFactory::get_instance()->delete_participant(participant_);
    }

    // Initialize the subscriber
    bool Init()
    {
        DomainParticipantQos participantQos;
        participantQos.name("Participant_subscriber");
        participant_ = DomainParticipantFactory::get_instance()->create_participant(0, participantQos);

        if (participant_ == nullptr)
        {
            return false;
        }

        // Register the type
        type_.register_type(participant_);

        topic_ = participant_->create_topic("CloudDataTopic", "CloudData", TOPIC_QOS_DEFAULT);
        if (topic_ == nullptr)
        {
            return false;
        }

        // Create the Subscriber
        subscriber_ = participant_->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr);
        if (subscriber_ == nullptr)
        {
            return false;
        }

        // Create the DataReader
        reader_ = subscriber_->create_datareader(topic_, DATAREADER_QOS_DEFAULT, &listener_);
        if (reader_ == nullptr)
        {
            return false;
        }
        return true;
    }

    void Run(uint32_t samples)
    {
        while (listener_.samples_ < samples)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }
}; // class CloudDataSubscriber

int main(int argc, char **argv)
{
    std::cout << "Starting subscriber." << std::endl;
    int samples = 10;

    CloudDataSubscriber *mysub = new CloudDataSubscriber();
    if (mysub->Init())
    {
        mysub->Run(static_cast<uint32_t>(samples));
    }
    delete mysub;
    return 0;
}
